
import assert from "assert";

import {
    getDevProvider, getProvider, setupProviders
} from "./create-provider.js";

import {
    Contract, ContractFactory, EventLog, isError, Typed
} from "../index.js";
import type { ContractEventPayload, ContractEventName, Log } from "../index.js";

setupProviders();

async function deploy(abi: Array<string>, bytecode: string):
  Promise<{ contract: Contract, contractSigner: Contract }> {
    const provider = getDevProvider();
    const signer = await provider.getSigner(0);

    const factory = new ContractFactory(abi, bytecode, signer);

    const contract = await factory.deploy();
    const addr = await contract.getAddress();

    await contract.waitForDeployment();

    return {
        contract: new Contract(addr, abi, provider),
        contractSigner: new Contract(addr, abi, signer),
    };
}

describe("Test Contract", function() {

    const abi = [
        "error CustomError1(uint256 code, string message)",

        "event EventUint256(uint256 indexed value)",
        "event EventAddress(address indexed value)",
        "event EventString(string value)",
        "event EventBytes(bytes value)",

        "function testCustomError1(bool pass, uint code, string calldata message) pure returns (uint256)",
        "function testErrorString(bool pass, string calldata message) pure returns (uint256)",
        "function testPanic(uint256 code) returns (uint256)",
        "function testEvent(uint256 valueUint256, address valueAddress, string valueString, bytes valueBytes) public",
        "function testCallAdd(uint256 a, uint256 b) pure returns (uint256 result)",
    ];

    const bytecode = "0x608060405234801561001057600080fd5b50610aec806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80633da613681461005c578063b206699b1461008c578063c8d6fda7146100bc578063db734248146100d8578063f24684e514610108575b600080fd5b6100766004803603810190610071919061048a565b610138565b60405161008391906104c6565b60405180910390f35b6100a660048036038101906100a1919061057e565b6102a1565b6040516100b391906104c6565b60405180910390f35b6100d660048036038101906100d19190610692565b6102f6565b005b6100f260048036038101906100ed9190610739565b6103e0565b6040516100ff91906104c6565b60405180910390f35b610122600480360381019061011d91906107ad565b610434565b60405161012f91906104c6565b60405180910390f35b600060018203610152576000610151576101506107ed565b5b5b6011820361016f5761ffff82610168919061084b565b905061029c565b6012820361019657816012610184919061084b565b8261018f91906108ae565b905061029c565b603182036101c75760018054806101b0576101af6108df565b5b600190038181906000526020600020016000905590555b603282036101f65760016005815481106101e4576101e361090e565b5b9060005260206000200154905061029c565b6041820361029857600065ffffffffffff67ffffffffffffffff8111156102205761021f61093d565b5b60405190808252806020026020018201604052801561024e5781602001602082028036833780820191505090505b509050611234816003815181106102685761026761090e565b5b602002602001018181525050806004815181106102885761028761090e565b5b602002602001015191505061029c565b8190505b919050565b600083838390916102e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102e09291906109ca565b60405180910390fd5b5050600190509392505050565b857f85c55bbb820e6d71c71f4894e57751de334b38c421f9c170b0e66d32eafea33760405160405180910390a28473ffffffffffffffffffffffffffffffffffffffff167f52cb491081609a3d8c50cb8d5c1395de748f65789fc66e140e795decadd86c3060405160405180910390a27f7240e2f75cccc64acf37f699b7cc2726ccd9c0ed8afeafdbf7911af78d077bad84846040516103979291906109ca565b60405180910390a17f06e852ba9138ee18ce13f482908b8634bc29d809282ea568cf505aca412b195e82826040516103d0929190610a2c565b60405180910390a1505050505050565b600084610428578383836040517f180c751a00000000000000000000000000000000000000000000000000000000815260040161041f93929190610a50565b60405180910390fd5b60019050949350505050565b600081836104429190610a82565b905092915050565b600080fd5b600080fd5b6000819050919050565b61046781610454565b811461047257600080fd5b50565b6000813590506104848161045e565b92915050565b6000602082840312156104a05761049f61044a565b5b60006104ae84828501610475565b91505092915050565b6104c081610454565b82525050565b60006020820190506104db60008301846104b7565b92915050565b60008115159050919050565b6104f6816104e1565b811461050157600080fd5b50565b600081359050610513816104ed565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261053e5761053d610519565b5b8235905067ffffffffffffffff81111561055b5761055a61051e565b5b60208301915083600182028301111561057757610576610523565b5b9250929050565b6000806000604084860312156105975761059661044a565b5b60006105a586828701610504565b935050602084013567ffffffffffffffff8111156105c6576105c561044f565b5b6105d286828701610528565b92509250509250925092565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610609826105de565b9050919050565b610619816105fe565b811461062457600080fd5b50565b60008135905061063681610610565b92915050565b60008083601f84011261065257610651610519565b5b8235905067ffffffffffffffff81111561066f5761066e61051e565b5b60208301915083600182028301111561068b5761068a610523565b5b9250929050565b600080600080600080608087890312156106af576106ae61044a565b5b60006106bd89828a01610475565b96505060206106ce89828a01610627565b955050604087013567ffffffffffffffff8111156106ef576106ee61044f565b5b6106fb89828a01610528565b9450945050606087013567ffffffffffffffff81111561071e5761071d61044f565b5b61072a89828a0161063c565b92509250509295509295509295565b600080600080606085870312156107535761075261044a565b5b600061076187828801610504565b945050602061077287828801610475565b935050604085013567ffffffffffffffff8111156107935761079261044f565b5b61079f87828801610528565b925092505092959194509250565b600080604083850312156107c4576107c361044a565b5b60006107d285828601610475565b92505060206107e385828601610475565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061085682610454565b915061086183610454565b92508282039050818111156108795761087861081c565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006108b982610454565b91506108c483610454565b9250826108d4576108d361087f565b5b828204905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006109a9838561096c565b93506109b683858461097d565b6109bf8361098c565b840190509392505050565b600060208201905081810360008301526109e581848661099d565b90509392505050565b600082825260208201905092915050565b6000610a0b83856109ee565b9350610a1883858461097d565b610a218361098c565b840190509392505050565b60006020820190508181036000830152610a478184866109ff565b90509392505050565b6000604082019050610a6560008301866104b7565b8181036020830152610a7881848661099d565b9050949350505050565b6000610a8d82610454565b9150610a9883610454565b9250828201905080821115610ab057610aaf61081c565b5b9291505056fea2646970667358221220ba2ca28af2ac3801bcc3e517c7faa2698586e3d29e95c252ae6c0a63a3b4d2e764736f6c63430008110033";

    const contractPromise = deploy(abi, bytecode);

    it("tests contract calls", async function() {
        this.timeout(10000);
        const { contract } = await contractPromise;

        assert.equal(await contract.testCallAdd(4, 5), BigInt(9), "testCallAdd(4, 5)");
        assert.equal(await contract.testCallAdd(6, 0), BigInt(6), "testCallAdd(6, 0)");
    });

    it("tests events", async function() {
        this.timeout(60000);

        const { contract, contractSigner } = await contractPromise;
        const addr = await contract.getAddress();

        const vUint256 = 42;
        const vAddrName = "tests.eth";
        const vAddr = "0x228568EA92aC5Bc281c1E30b1893735c60a139F1";
        const vString = "Hello";
        const vBytes = "0x12345678";

        let hash: null | string = null;

        // Test running a listener for a specific event
        const specificEvent = new Promise((resolve, reject) => {
            contract.on("EventUint256", async (value, event) => {
                // Triggered by someone else
                if (hash == null || hash !== event.log.transactionHash) { return; }

                try {
                    assert.equal(event.filter, "EventUint256", "event.filter");
                    assert.equal(event.fragment.name, "EventUint256", "event.fragment.name");
                    assert.equal(event.log.address, addr, "event.log.address");
                    assert.equal(event.args.length, 1, "event.args.length");
                    assert.equal(event.args[0], BigInt(42), "event.args[0]");

                    const count = await contract.listenerCount("EventUint256");
                    await event.removeListener();
                    assert.equal(await contract.listenerCount("EventUint256"), count - 1, "decrement event count");

                    resolve(null);
                } catch (e) {
                    event.removeListener();
                    reject(e);
                }
            });
        });

        // Test running a listener on all (i.e. "*") events
        const allEvents = new Promise((resolve, reject) => {
            const waitingFor: Record<string, any> = {
                EventUint256: vUint256,
                EventAddress: vAddr,
                EventString: vString,
                EventBytes: vBytes
            };

            contract.on("*", (event: ContractEventPayload) => {
                // Triggered by someone else
                if (hash == null || hash !== event.log.transactionHash) { return; }
                try {
                    const name = event.eventName;

                    assert.equal(event.args[0], waitingFor[name], `${ name }`);
                    delete waitingFor[name];

                    if (Object.keys(waitingFor).length === 0) {
                        event.removeListener();
                        resolve(null);
                    }

                } catch (error) {
                    reject(error);
                }
            });

        });

        // Send a transaction to trigger some events
        const tx = await contractSigner.testEvent(vUint256, vAddr, vString, vBytes);
        hash = tx.hash;

        const checkEvent = (filter: ContractEventName, event: EventLog | Log) => {
            const values: Record<string, any> = {
                EventUint256: vUint256,
                EventString: vString,
                EventAddress: vAddr,
                EventBytes: vBytes
            };

            assert.ok(event instanceof EventLog, `queryFilter(${ filter }):isEventLog`);

            const name = event.eventName;

            assert.equal(event.address, addr, `queryFilter(${ filter }):address`);
            assert.equal(event.args[0], values[name], `queryFilter(${ filter }):args[0]`);
        };

        const checkEventFilter = async (filter: ContractEventName) => {
            const events = (await contract.queryFilter(filter, -10)).filter((e) => (e.transactionHash === hash));
            assert.equal(events.length, 1, `queryFilter(${ filter }).length`);
            checkEvent(filter, events[0]);
            return events[0];
        };

        const receipt = await tx.wait();

        // Check the logs in the receipt
        for (const log of receipt.logs) { checkEvent("receipt", log); }

        // Various options for queryFilter
        await checkEventFilter("EventUint256");
        await checkEventFilter([ "EventUint256" ]);
        await checkEventFilter([ [ "EventUint256" ] ]);
        await checkEventFilter("EventUint256(uint)");
        await checkEventFilter([ "EventUint256(uint)" ]);
        await checkEventFilter([ [ "EventUint256(uint)" ] ]);
        await checkEventFilter([ [ "EventUint256", "EventUint256(uint)" ] ]);
        await checkEventFilter("0x85c55bbb820e6d71c71f4894e57751de334b38c421f9c170b0e66d32eafea337");

        // Query by Event
        await checkEventFilter(contract.filters.EventUint256);

        // Query by Deferred Topic Filter; address
        await checkEventFilter(contract.filters.EventUint256(vUint256));

        // Query by Deferred Topic Filter; address
        await checkEventFilter(contract.filters.EventAddress(vAddr));

        // Query by Deferred Topic Filter; ENS name => address
        await checkEventFilter(contract.filters.EventAddress(vAddrName));

        // Multiple Methods
        {
            const filter = [ [ "EventUint256", "EventString" ] ];
            const events = (await contract.queryFilter(filter, -10)).filter((e) => (e.transactionHash === hash));
            assert.equal(events.length, 2, `queryFilter(${ filter }).length`);

            for (const event of events) { checkEvent(filter, event); }
        }

        await specificEvent;
        await allEvents;
    });

    it("tests the _in_ operator for functions", function() {
        const addr = "0x24264C81146c23bbF83Db1d3F87d2111C2935Ffa" // Sepolia
        const contract = new Contract(addr, abi);

        assert.equal("testCallAdd" in contract, true, "has(testCallAdd)");
        assert.equal("nonExist" in contract, false, "has(nonExist)");

        {
            const sig = "function testCallAdd(uint256 a, uint256 b) pure returns (uint256 result)";
            assert.equal(sig in contract, true, `has(${ sig })`);
            assert.equal("function nonExist()" in contract, false, "has(function nonExist())");
        }

        assert.equal("0xf24684e5" in contract, true, "has(0xf24684e5)");
        assert.equal("0xbad01234" in contract, false, "has(0xbad01234)");
    });

    it("tests the _in_ operator for events", function() {
        const addr = "0x24264C81146c23bbF83Db1d3F87d2111C2935Ffa" // Sepolia
        const contract = new Contract(addr, abi);

        assert.equal("EventUint256" in contract.filters, true, "has(EventUint256)");
        assert.equal("NonExist" in contract.filters, false, "has(NonExist)");

        {
            const sig = "event EventUint256(uint256 indexed value)";
            assert.equal(sig in contract.filters, true, `has(${ sig })`);
            assert.equal("event NonExist()" in contract.filters, false, "has(event NonExist())");
        }

        {
            const hash = "0x85c55bbb820e6d71c71f4894e57751de334b38c421f9c170b0e66d32eafea337";
            const badHash = "0xbad01234567890ffbad01234567890ffbad01234567890ffbad01234567890ff";
            assert.equal(hash in contract.filters, true, `has(${ hash })`);
            assert.equal(badHash in contract.filters, false, `has(${ badHash })`);
        }

    });

});

describe("Test Typed Contract Interaction", function() {
    const tests: Array<{ types: Array<string>, valueFunc: (t: string) => any }> = [
        {
            types: [ "uint8", "uint16", "uint24", "uint32", "uint40", "uint48", "uint56", "uint64", "uint72", "uint80", "uint88", "uint96", "uint104", "uint112", "uint120", "uint128", "uint136", "uint144", "uint152", "uint160", "uint168", "uint176", "uint184", "uint192", "uint200", "uint208", "uint216", "uint224", "uint232", "uint240", "uint248", "uint256", "int8", "int16", "int24", "int32", "int40", "int48", "int56", "int64", "int72", "int80", "int88", "int96", "int104", "int112", "int120", "int128", "int136", "int144", "int152", "int160", "int168", "int176", "int184", "int192", "int200", "int208", "int216", "int224", "int232", "int240", "int248", "int256" ],
            valueFunc: (type: string) => { return 42; }
        },
        {
            types: [
                "bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11", "bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21", "bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31", "bytes32",
                "bytes"
            ],
            valueFunc: (type: string) => {
                const length = type.substring(5);
                if (length) {
                    const value = new Uint8Array(parseInt(length));
                    value.fill(42);
                    return value;
                }
                return "0x123456";
            }
        }, {
            types: [ "bool" ],
            valueFunc: (type: string) => { return true; }
        }, {
            types: [ "address" ],
            valueFunc: (type: string) => { return "0x643aA0A61eADCC9Cc202D1915D942d35D005400C"; }
        }, {
            types: [ "string" ],
            valueFunc: (type: string) => { return "someString"; }
        }
    ];

    const abi: Array<string> = [ ];
    for (let i = 1; i <= 32; i++) {
        abi.push(`function testTyped(uint${ i * 8 }) public pure returns (string memory)`);
        abi.push(`function testTyped(int${ i * 8 }) public pure returns (string memory)`);
        abi.push(`function testTyped(bytes${ i }) public pure returns (string memory)`);
    }
    abi.push(`function testTyped(address) public pure returns (string memory)`);
    abi.push(`function testTyped(bool) public pure returns (string memory)`);
    abi.push(`function testTyped(bytes memory) public pure returns (string memory)`);
    abi.push(`function testTyped(string memory) public pure returns (string memory)`);

    const bytecode = "0x608060405234801561001057600080fd5b50615fdf80620000216000396000f3fe608060405234801561001057600080fd5b506004361061059c5760003560e01c8063a202e8c0116102e5578063cec31ded1161018d578063e96add8c116100f4578063f3164597116100ad578063f85a090311610087578063f85a0903146117a1578063fa4d99c9146117d1578063fbf3e13a14611801578063fd1652cb146118315761059c565b8063f316459714611711578063f440761714611741578063f7b0e3d5146117715761059c565b8063e96add8c146115f1578063e9da42ca14611621578063eb01940114611651578063eecd112914611681578063ef4483be146116b1578063f0fb21ff146116e15761059c565b8063d92e6c0611610146578063d92e6c06146114d1578063dad2ef4f14611501578063dafbc72414611531578063e0fa6ea114611561578063e137fb7014611591578063e8fb9223146115c15761059c565b8063cec31ded146113b1578063d0e03b07146113e1578063d20f679a14611411578063d4b789d814611441578063d4c0c48614611471578063d7d73984146114a15761059c565b8063bbeb25e01161024c578063c233fd3311610205578063c84ad580116101df578063c84ad580146112f1578063c8ddda4a14611321578063cc69cf9714611351578063ccd94f98146113815761059c565b8063c233fd3314611261578063c639604b14611291578063c847fe3b146112c15761059c565b8063bbeb25e014611141578063bccdc9c414611171578063bf01c7f9146111a1578063c081607b146111d1578063c1397e9914611201578063c1847bee146112315761059c565b8063ad87e2e11161029e578063ad87e2e114611021578063b429b23c14611051578063b7bab28614611081578063b860f47f146110b1578063ba607fac146110e1578063bb73447f146111115761059c565b8063a202e8c014610f01578063a3ab9e7a14610f31578063a6496f6214610f61578063a925e76214610f91578063a95d606e14610fc1578063aa8de94614610ff15761059c565b806351e0489a116104485780637c9affcb116103af578063891a099d116103685780639df2391c116103425780639df2391c14610e415780639faf388f14610e715780639fcbede614610ea1578063a178b80014610ed15761059c565b8063891a099d14610db15780638e4884d114610de15780639ded4e2b14610e115761059c565b80637c9affcb14610c915780637fcc3d5d14610cc1578063803bf56d14610cf157806381c4b4cc14610d215780638275b29014610d5157806387ad751514610d815761059c565b80636913a82b116104015780636913a82b14610b715780636a678a7514610ba157806375b5533714610bd1578063769008e414610c015780637a3058c114610c315780637bc705c114610c615761059c565b806351e0489a14610a5157806357301b0214610a815780635793d40814610ab15780636357d3b714610ae1578063659665f914610b11578063679b530214610b415761059c565b80632a0caa6a11610507578063429545a4116104c05780634961a7ce1161049a5780634961a7ce146109915780634a3ccd5f146109c15780634fb7d4bd146109f157806350fbcfb614610a215761059c565b8063429545a41461090157806343c84f331461093157806346d79d13146109615761059c565b80632a0caa6a146107e15780632a97dfbf146108115780632f0d528b1461084157806333c064151461087157806334bdbab7146108a15780634029cdee146108d15761059c565b80631b46fa94116105595780631b46fa94146106c15780631eb765fd146106f157806325bc2c821461072157806325cb6b9a1461075157806326888cda1461078157806328b01d81146107b15761059c565b806303290450146105a15780630e9ba011146105d15780630f84821114610601578063107810d5146106315780631400a02f146106615780631a762a3a14610691575b600080fd5b6105bb60048036038101906105b69190613145565b611861565b6040516105c89190613202565b60405180910390f35b6105eb60048036038101906105e6919061327c565b6118a0565b6040516105f89190613202565b60405180910390f35b61061b600480360381019061061691906132e2565b6118df565b6040516106289190613202565b60405180910390f35b61064b6004803603810190610646919061335d565b61191e565b6040516106589190613202565b60405180910390f35b61067b600480360381019061067691906133e2565b61195d565b6040516106889190613202565b60405180910390f35b6106ab60048036038101906106a69190613467565b61199c565b6040516106b89190613202565b60405180910390f35b6106db60048036038101906106d691906134cd565b6119db565b6040516106e89190613202565b60405180910390f35b61070b6004803603810190610706919061354b565b611a1a565b6040516107189190613202565b60405180910390f35b61073b600480360381019061073691906135d0565b611a59565b6040516107489190613202565b60405180910390f35b61076b60048036038101906107669190613636565b611a98565b6040516107789190613202565b60405180910390f35b61079b600480360381019061079691906136b6565b611ad7565b6040516107a89190613202565b60405180910390f35b6107cb60048036038101906107c69190613732565b611b16565b6040516107d89190613202565b60405180910390f35b6107fb60048036038101906107f691906137aa565b611b55565b6040516108089190613202565b60405180910390f35b61082b60048036038101906108269190613810565b611b94565b6040516108389190613202565b60405180910390f35b61085b6004803603810190610856919061387f565b611bd3565b6040516108689190613202565b60405180910390f35b61088b60048036038101906108869190613904565b611c12565b6040516108989190613202565b60405180910390f35b6108bb60048036038101906108b69190613989565b611c51565b6040516108c89190613202565b60405180910390f35b6108eb60048036038101906108e691906139ef565b611c90565b6040516108f89190613202565b60405180910390f35b61091b60048036038101906109169190613a74565b611ccf565b6040516109289190613202565b60405180910390f35b61094b60048036038101906109469190613ae6565b611d0e565b6040516109589190613202565b60405180910390f35b61097b60048036038101906109769190613b4c565b611d4d565b6040516109889190613202565b60405180910390f35b6109ab60048036038101906109a69190613bcd565b611d8c565b6040516109b89190613202565b60405180910390f35b6109db60048036038101906109d69190613c33565b611dcb565b6040516109e89190613202565b60405180910390f35b610a0b6004803603810190610a069190613c99565b611e0a565b604051610a189190613202565b60405180910390f35b610a3b6004803603810190610a369190613cff565b611e49565b604051610a489190613202565b60405180910390f35b610a6b6004803603810190610a669190613d84565b611e88565b604051610a789190613202565b60405180910390f35b610a9b6004803603810190610a969190613e01565b611ec7565b604051610aa89190613202565b60405180910390f35b610acb6004803603810190610ac69190613e86565b611f06565b604051610ad89190613202565b60405180910390f35b610afb6004803603810190610af69190613eee565b611f45565b604051610b089190613202565b60405180910390f35b610b2b6004803603810190610b269190613f54565b611f84565b604051610b389190613202565b60405180910390f35b610b5b6004803603810190610b569190613fd9565b611fc3565b604051610b689190613202565b60405180910390f35b610b8b6004803603810190610b869190614040565b612002565b604051610b989190613202565b60405180910390f35b610bbb6004803603810190610bb691906140c5565b612041565b604051610bc89190613202565b60405180910390f35b610beb6004803603810190610be6919061414a565b612080565b604051610bf89190613202565b60405180910390f35b610c1b6004803603810190610c1691906141b0565b6120bf565b604051610c289190613202565b60405180910390f35b610c4b6004803603810190610c469190614216565b6120fe565b604051610c589190613202565b60405180910390f35b610c7b6004803603810190610c76919061429b565b61213d565b604051610c889190613202565b60405180910390f35b610cab6004803603810190610ca69190614301565b61217c565b604051610cb89190613202565b60405180910390f35b610cdb6004803603810190610cd69190614386565b6121bb565b604051610ce89190613202565b60405180910390f35b610d0b6004803603810190610d06919061440b565b6121fa565b604051610d189190613202565b60405180910390f35b610d3b6004803603810190610d369190614475565b612239565b604051610d489190613202565b60405180910390f35b610d6b6004803603810190610d6691906144e9565b612278565b604051610d789190613202565b60405180910390f35b610d9b6004803603810190610d969190614563565b6122b7565b604051610da89190613202565b60405180910390f35b610dcb6004803603810190610dc691906145c9565b6122f6565b604051610dd89190613202565b60405180910390f35b610dfb6004803603810190610df6919061464d565b612335565b604051610e089190613202565b60405180910390f35b610e2b6004803603810190610e2691906146d2565b612374565b604051610e389190613202565b60405180910390f35b610e5b6004803603810190610e569190614738565b6123b3565b604051610e689190613202565b60405180910390f35b610e8b6004803603810190610e86919061479e565b6123f2565b604051610e989190613202565b60405180910390f35b610ebb6004803603810190610eb69190614830565b612431565b604051610ec89190613202565b60405180910390f35b610eeb6004803603810190610ee691906148d3565b612471565b604051610ef89190613202565b60405180910390f35b610f1b6004803603810190610f169190614946565b6124b0565b604051610f289190613202565b60405180910390f35b610f4b6004803603810190610f4691906149cb565b6124ef565b604051610f589190613202565b60405180910390f35b610f7b6004803603810190610f769190614a31565b61252e565b604051610f889190613202565b60405180910390f35b610fab6004803603810190610fa69190614a97565b61256d565b604051610fb89190613202565b60405180910390f35b610fdb6004803603810190610fd69190614b1c565b6125ac565b604051610fe89190613202565b60405180910390f35b61100b60048036038101906110069190614b82565b6125eb565b6040516110189190613202565b60405180910390f35b61103b60048036038101906110369190614c01565b61262a565b6040516110489190613202565b60405180910390f35b61106b60048036038101906110669190614c71565b612669565b6040516110789190613202565b60405180910390f35b61109b60048036038101906110969190614cf6565b6126a8565b6040516110a89190613202565b60405180910390f35b6110cb60048036038101906110c69190614d7b565b6126e7565b6040516110d89190613202565b60405180910390f35b6110fb60048036038101906110f69190614de1565b612726565b6040516111089190613202565b60405180910390f35b61112b60048036038101906111269190614e4e565b612765565b6040516111389190613202565b60405180910390f35b61115b60048036038101906111569190614ed9565b6127a4565b6040516111689190613202565b60405180910390f35b61118b60048036038101906111869190614f5e565b6127e3565b6040516111989190613202565b60405180910390f35b6111bb60048036038101906111b69190614fe3565b612822565b6040516111c89190613202565b60405180910390f35b6111eb60048036038101906111e69190615049565b612861565b6040516111f89190613202565b60405180910390f35b61121b600480360381019061121691906150bf565b6128a0565b6040516112289190613202565b60405180910390f35b61124b60048036038101906112469190615141565b6128df565b6040516112589190613202565b60405180910390f35b61127b600480360381019061127691906151c6565b61291e565b6040516112889190613202565b60405180910390f35b6112ab60048036038101906112a6919061524b565b61295d565b6040516112b89190613202565b60405180910390f35b6112db60048036038101906112d691906152b6565b61299c565b6040516112e89190613202565b60405180910390f35b61130b6004803603810190611306919061531c565b6129db565b6040516113189190613202565b60405180910390f35b61133b6004803603810190611336919061538d565b612a1a565b6040516113489190613202565b60405180910390f35b61136b600480360381019061136691906153e6565b612a59565b6040516113789190613202565b60405180910390f35b61139b6004803603810190611396919061544c565b612a98565b6040516113a89190613202565b60405180910390f35b6113cb60048036038101906113c691906154ba565b612ad7565b6040516113d89190613202565b60405180910390f35b6113fb60048036038101906113f69190615520565b612b16565b6040516114089190613202565b60405180910390f35b61142b60048036038101906114269190615583565b612b55565b6040516114389190613202565b60405180910390f35b61145b600480360381019061145691906155e6565b612b94565b6040516114689190613202565b60405180910390f35b61148b6004803603810190611486919061564c565b612bd3565b6040516114989190613202565b60405180910390f35b6114bb60048036038101906114b691906156d1565b612c12565b6040516114c89190613202565b60405180910390f35b6114eb60048036038101906114e6919061573d565b612c51565b6040516114f89190613202565b60405180910390f35b61151b600480360381019061151691906157c2565b612c90565b6040516115289190613202565b60405180910390f35b61154b60048036038101906115469190615828565b612ccf565b6040516115589190613202565b60405180910390f35b61157b6004803603810190611576919061588e565b612d0e565b6040516115889190613202565b60405180910390f35b6115ab60048036038101906115a69190615903565b612d4d565b6040516115b89190613202565b60405180910390f35b6115db60048036038101906115d69190615969565b612d8c565b6040516115e89190613202565b60405180910390f35b61160b600480360381019061160691906159ce565b612dcb565b6040516116189190613202565b60405180910390f35b61163b60048036038101906116369190615a53565b612e0a565b6040516116489190613202565b60405180910390f35b61166b60048036038101906116669190615ad8565b612e49565b6040516116789190613202565b60405180910390f35b61169b60048036038101906116969190615b3b565b612e88565b6040516116a89190613202565b60405180910390f35b6116cb60048036038101906116c69190615bc0565b612ec7565b6040516116d89190613202565b60405180910390f35b6116fb60048036038101906116f69190615c26565b612f06565b6040516117089190613202565b60405180910390f35b61172b60048036038101906117269190615c9d565b612f45565b6040516117389190613202565b60405180910390f35b61175b60048036038101906117569190615d20565b612f84565b6040516117689190613202565b60405180910390f35b61178b60048036038101906117869190615da6565b612fc4565b6040516117989190613202565b60405180910390f35b6117bb60048036038101906117b69190615e2b565b613003565b6040516117c89190613202565b60405180910390f35b6117eb60048036038101906117e69190615e91565b613042565b6040516117f89190613202565b60405180910390f35b61181b60048036038101906118169190615ef7565b613081565b6040516118289190613202565b60405180910390f35b61184b60048036038101906118469190615f7c565b6130c0565b6040516118589190613202565b60405180910390f35b60606040518060400160405280600681526020017f75696e74333200000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733234000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7432303800000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313736000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733400000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733900000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7437320000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74323030000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733230000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7433320000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74323136000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313834000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313532000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7432343800000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74383000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733238000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733330000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431323000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733231000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313034000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7439360000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74323234000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431363800000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7432303000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431383400000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733134000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313932000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733200000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74323400000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431313200000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733600000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74313600000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733300000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733137000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7432323400000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431323800000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733138000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431303400000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733700000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733235000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74343000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313230000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313638000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431333600000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74323438000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733800000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431393200000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7432313600000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f6279746573000000000000000000000000000000000000000000000000000000815250905092915050565b60606040518060400160405280600781526020017f75696e74323430000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313132000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733239000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7438300000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431373600000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733331000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f75696e74380000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74323038000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74383800000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733130000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733136000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7438380000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74363400000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f61646472657373000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733132000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733237000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7436340000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313336000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74323332000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733236000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733500000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74343800000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7434300000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74393600000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313630000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600481526020017f696e7438000000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74373200000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7432333200000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733332000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74323536000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7431360000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733135000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f75696e74353600000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733233000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7432343000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431363000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313238000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7434380000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600481526020017f626f6f6c000000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733133000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733232000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7432353600000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733139000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431353200000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f75696e74313434000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f737472696e670000000000000000000000000000000000000000000000000000815250905092915050565b60606040518060400160405280600581526020017f696e7432340000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600781526020017f62797465733131000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600581526020017f696e7435360000000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f696e7431343400000000000000000000000000000000000000000000000000008152509050919050565b60606040518060400160405280600681526020017f62797465733100000000000000000000000000000000000000000000000000008152509050919050565b600080fd5b600080fd5b600063ffffffff82169050919050565b61312281613109565b811461312d57600080fd5b50565b60008135905061313f81613119565b92915050565b60006020828403121561315b5761315a6130ff565b5b600061316984828501613130565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156131ac578082015181840152602081019050613191565b60008484015250505050565b6000601f19601f8301169050919050565b60006131d482613172565b6131de818561317d565b93506131ee81856020860161318e565b6131f7816131b8565b840191505092915050565b6000602082019050818103600083015261321c81846131c9565b905092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000082169050919050565b61325981613224565b811461326457600080fd5b50565b60008135905061327681613250565b92915050565b600060208284031215613292576132916130ff565b5b60006132a084828501613267565b91505092915050565b60008160190b9050919050565b6132bf816132a9565b81146132ca57600080fd5b50565b6000813590506132dc816132b6565b92915050565b6000602082840312156132f8576132f76130ff565b5b6000613306848285016132cd565b91505092915050565b600075ffffffffffffffffffffffffffffffffffffffffffff82169050919050565b61333a8161330f565b811461334557600080fd5b50565b60008135905061335781613331565b92915050565b600060208284031215613373576133726130ff565b5b600061338184828501613348565b91505092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6133bf8161338a565b81146133ca57600080fd5b50565b6000813590506133dc816133b6565b92915050565b6000602082840312156133f8576133f76130ff565b5b6000613406848285016133cd565b91505092915050565b60007fffffffffffffffffff000000000000000000000000000000000000000000000082169050919050565b6134448161340f565b811461344f57600080fd5b50565b6000813590506134618161343b565b92915050565b60006020828403121561347d5761347c6130ff565b5b600061348b84828501613452565b91505092915050565b60008160080b9050919050565b6134aa81613494565b81146134b557600080fd5b50565b6000813590506134c7816134a1565b92915050565b6000602082840312156134e3576134e26130ff565b5b60006134f1848285016134b8565b91505092915050565b600078ffffffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b613528816134fa565b811461353357600080fd5b50565b6000813590506135458161351f565b92915050565b600060208284031215613561576135606130ff565b5b600061356f84828501613536565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082169050919050565b6135ad81613578565b81146135b857600080fd5b50565b6000813590506135ca816135a4565b92915050565b6000602082840312156135e6576135e56130ff565b5b60006135f4848285016135bb565b91505092915050565b60008160030b9050919050565b613613816135fd565b811461361e57600080fd5b50565b6000813590506136308161360a565b92915050565b60006020828403121561364c5761364b6130ff565b5b600061365a84828501613621565b91505092915050565b60007affffffffffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b61369381613663565b811461369e57600080fd5b50565b6000813590506136b08161368a565b92915050565b6000602082840312156136cc576136cb6130ff565b5b60006136da848285016136a1565b91505092915050565b600076ffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b61370f816136e3565b811461371a57600080fd5b50565b60008135905061372c81613706565b92915050565b600060208284031215613748576137476130ff565b5b60006137568482850161371d565b91505092915050565b600072ffffffffffffffffffffffffffffffffffffff82169050919050565b6137878161375f565b811461379257600080fd5b50565b6000813590506137a48161377e565b92915050565b6000602082840312156137c0576137bf6130ff565b5b60006137ce84828501613795565b91505092915050565b600081601e0b9050919050565b6137ed816137d7565b81146137f857600080fd5b50565b60008135905061380a816137e4565b92915050565b600060208284031215613826576138256130ff565b5b6000613834848285016137fb565b91505092915050565b600069ffffffffffffffffffff82169050919050565b61385c8161383d565b811461386757600080fd5b50565b60008135905061387981613853565b92915050565b600060208284031215613895576138946130ff565b5b60006138a38482850161386a565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000082169050919050565b6138e1816138ac565b81146138ec57600080fd5b50565b6000813590506138fe816138d8565b92915050565b60006020828403121561391a576139196130ff565b5b6000613928848285016138ef565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000082169050919050565b61396681613931565b811461397157600080fd5b50565b6000813590506139838161395d565b92915050565b60006020828403121561399f5761399e6130ff565b5b60006139ad84828501613974565b91505092915050565b600081600e0b9050919050565b6139cc816139b6565b81146139d757600080fd5b50565b6000813590506139e9816139c3565b92915050565b600060208284031215613a0557613a046130ff565b5b6000613a13848285016139da565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffff000000000000000000000082169050919050565b613a5181613a1c565b8114613a5c57600080fd5b50565b600081359050613a6e81613a48565b92915050565b600060208284031215613a8a57613a896130ff565b5b6000613a9884828501613a5f565b91505092915050565b60006cffffffffffffffffffffffffff82169050919050565b613ac381613aa1565b8114613ace57600080fd5b50565b600081359050613ae081613aba565b92915050565b600060208284031215613afc57613afb6130ff565b5b6000613b0a84828501613ad1565b91505092915050565b600081600b0b9050919050565b613b2981613b13565b8114613b3457600080fd5b50565b600081359050613b4681613b20565b92915050565b600060208284031215613b6257613b616130ff565b5b6000613b7084828501613b37565b91505092915050565b60007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b613baa81613b79565b8114613bb557600080fd5b50565b600081359050613bc781613ba1565b92915050565b600060208284031215613be357613be26130ff565b5b6000613bf184828501613bb8565b91505092915050565b60008160140b9050919050565b613c1081613bfa565b8114613c1b57600080fd5b50565b600081359050613c2d81613c07565b92915050565b600060208284031215613c4957613c486130ff565b5b6000613c5784828501613c1e565b91505092915050565b60008160180b9050919050565b613c7681613c60565b8114613c8157600080fd5b50565b600081359050613c9381613c6d565b92915050565b600060208284031215613caf57613cae6130ff565b5b6000613cbd84828501613c84565b91505092915050565b60008160160b9050919050565b613cdc81613cc6565b8114613ce757600080fd5b50565b600081359050613cf981613cd3565b92915050565b600060208284031215613d1557613d146130ff565b5b6000613d2384828501613cea565b91505092915050565b60007fffffffffffffffffffffffffffff00000000000000000000000000000000000082169050919050565b613d6181613d2c565b8114613d6c57600080fd5b50565b600081359050613d7e81613d58565b92915050565b600060208284031215613d9a57613d996130ff565b5b6000613da884828501613d6f565b91505092915050565b600077ffffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b613dde81613db1565b8114613de957600080fd5b50565b600081359050613dfb81613dd5565b92915050565b600060208284031215613e1757613e166130ff565b5b6000613e2584828501613dec565b91505092915050565b60007fffff00000000000000000000000000000000000000000000000000000000000082169050919050565b613e6381613e2e565b8114613e6e57600080fd5b50565b600081359050613e8081613e5a565b92915050565b600060208284031215613e9c57613e9b6130ff565b5b6000613eaa84828501613e71565b91505092915050565b600062ffffff82169050919050565b613ecb81613eb3565b8114613ed657600080fd5b50565b600081359050613ee881613ec2565b92915050565b600060208284031215613f0457613f036130ff565b5b6000613f1284828501613ed9565b91505092915050565b600081600d0b9050919050565b613f3181613f1b565b8114613f3c57600080fd5b50565b600081359050613f4e81613f28565b92915050565b600060208284031215613f6a57613f696130ff565b5b6000613f7884828501613f3f565b91505092915050565b60007fffffffffffff000000000000000000000000000000000000000000000000000082169050919050565b613fb681613f81565b8114613fc157600080fd5b50565b600081359050613fd381613fad565b92915050565b600060208284031215613fef57613fee6130ff565b5b6000613ffd84828501613fc4565b91505092915050565b600061ffff82169050919050565b61401d81614006565b811461402857600080fd5b50565b60008135905061403a81614014565b92915050565b600060208284031215614056576140556130ff565b5b60006140648482850161402b565b91505092915050565b60007fffffff000000000000000000000000000000000000000000000000000000000082169050919050565b6140a28161406d565b81146140ad57600080fd5b50565b6000813590506140bf81614099565b92915050565b6000602082840312156140db576140da6130ff565b5b60006140e9848285016140b0565b91505092915050565b60007fffffffffffffffffffffffffffffffffff00000000000000000000000000000082169050919050565b614127816140f2565b811461413257600080fd5b50565b6000813590506141448161411e565b92915050565b6000602082840312156141605761415f6130ff565b5b600061416e84828501614135565b91505092915050565b600081601b0b9050919050565b61418d81614177565b811461419857600080fd5b50565b6000813590506141aa81614184565b92915050565b6000602082840312156141c6576141c56130ff565b5b60006141d48482850161419b565b91505092915050565b600081600f0b9050919050565b6141f3816141dd565b81146141fe57600080fd5b50565b600081359050614210816141ea565b92915050565b60006020828403121561422c5761422b6130ff565b5b600061423a84828501614201565b91505092915050565b60007fffffffffffffffffffffffffffffffffffff000000000000000000000000000082169050919050565b61427881614243565b811461428357600080fd5b50565b6000813590506142958161426f565b92915050565b6000602082840312156142b1576142b06130ff565b5b60006142bf84828501614286565b91505092915050565b600081600c0b9050919050565b6142de816142c8565b81146142e957600080fd5b50565b6000813590506142fb816142d5565b92915050565b600060208284031215614317576143166130ff565b5b6000614325848285016142ec565b91505092915050565b60007fffffffffffffff0000000000000000000000000000000000000000000000000082169050919050565b6143638161432e565b811461436e57600080fd5b50565b6000813590506143808161435a565b92915050565b60006020828403121561439c5761439b6130ff565b5b60006143aa84828501614371565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000082169050919050565b6143e8816143b3565b81146143f357600080fd5b50565b600081359050614405816143df565b92915050565b600060208284031215614421576144206130ff565b5b600061442f848285016143f6565b91505092915050565b600064ffffffffff82169050919050565b61445281614438565b811461445d57600080fd5b50565b60008135905061446f81614449565b92915050565b60006020828403121561448b5761448a6130ff565b5b600061449984828501614460565b91505092915050565b60006effffffffffffffffffffffffffffff82169050919050565b6144c6816144a2565b81146144d157600080fd5b50565b6000813590506144e3816144bd565b92915050565b6000602082840312156144ff576144fe6130ff565b5b600061450d848285016144d4565b91505092915050565b600074ffffffffffffffffffffffffffffffffffffffffff82169050919050565b61454081614516565b811461454b57600080fd5b50565b60008135905061455d81614537565b92915050565b600060208284031215614579576145786130ff565b5b60006145878482850161454e565b91505092915050565b60008160100b9050919050565b6145a681614590565b81146145b157600080fd5b50565b6000813590506145c38161459d565b92915050565b6000602082840312156145df576145de6130ff565b5b60006145ed848285016145b4565b91505092915050565b60007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b61462a816145f6565b811461463557600080fd5b50565b60008135905061464781614621565b92915050565b600060208284031215614663576146626130ff565b5b600061467184828501614638565b91505092915050565b60007fffffffffffffffff00000000000000000000000000000000000000000000000082169050919050565b6146af8161467a565b81146146ba57600080fd5b50565b6000813590506146cc816146a6565b92915050565b6000602082840312156146e8576146e76130ff565b5b60006146f6848285016146bd565b91505092915050565b60008160170b9050919050565b614715816146ff565b811461472057600080fd5b50565b6000813590506147328161470c565b92915050565b60006020828403121561474e5761474d6130ff565b5b600061475c84828501614723565b91505092915050565b600081601a0b9050919050565b61477b81614765565b811461478657600080fd5b50565b60008135905061479881614772565b92915050565b6000602082840312156147b4576147b36130ff565b5b60006147c284828501614789565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126147f0576147ef6147cb565b5b8235905067ffffffffffffffff81111561480d5761480c6147d0565b5b602083019150836001820283011115614829576148286147d5565b5b9250929050565b60008060208385031215614847576148466130ff565b5b600083013567ffffffffffffffff81111561486557614864613104565b5b614871858286016147da565b92509250509250929050565b60007dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b6148b08161487d565b81146148bb57600080fd5b50565b6000813590506148cd816148a7565b92915050565b6000602082840312156148e9576148e86130ff565b5b60006148f7848285016148be565b91505092915050565b60006dffffffffffffffffffffffffffff82169050919050565b61492381614900565b811461492e57600080fd5b50565b6000813590506149408161491a565b92915050565b60006020828403121561495c5761495b6130ff565b5b600061496a84828501614931565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000082169050919050565b6149a881614973565b81146149b357600080fd5b50565b6000813590506149c58161499f565b92915050565b6000602082840312156149e1576149e06130ff565b5b60006149ef848285016149b6565b91505092915050565b60008160090b9050919050565b614a0e816149f8565b8114614a1957600080fd5b50565b600081359050614a2b81614a05565b92915050565b600060208284031215614a4757614a466130ff565b5b6000614a5584828501614a1c565b91505092915050565b60008160150b9050919050565b614a7481614a5e565b8114614a7f57600080fd5b50565b600081359050614a9181614a6b565b92915050565b600060208284031215614aad57614aac6130ff565b5b6000614abb84828501614a82565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082169050919050565b614af981614ac4565b8114614b0457600080fd5b50565b600081359050614b1681614af0565b92915050565b600060208284031215614b3257614b316130ff565b5b6000614b4084828501614b07565b91505092915050565b600060ff82169050919050565b614b5f81614b49565b8114614b6a57600080fd5b50565b600081359050614b7c81614b56565b92915050565b600060208284031215614b9857614b976130ff565b5b6000614ba684828501614b6d565b91505092915050565b600079ffffffffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b614bde81614baf565b8114614be957600080fd5b50565b600081359050614bfb81614bd5565b92915050565b600060208284031215614c1757614c166130ff565b5b6000614c2584828501614bec565b91505092915050565b60006affffffffffffffffffffff82169050919050565b614c4e81614c2e565b8114614c5957600080fd5b50565b600081359050614c6b81614c45565b92915050565b600060208284031215614c8757614c866130ff565b5b6000614c9584828501614c5c565b91505092915050565b60007fffffffffffffffffffff0000000000000000000000000000000000000000000082169050919050565b614cd381614c9e565b8114614cde57600080fd5b50565b600081359050614cf081614cca565b92915050565b600060208284031215614d0c57614d0b6130ff565b5b6000614d1a84828501614ce1565b91505092915050565b60007fffffffffffffffffffffffffffffffff0000000000000000000000000000000082169050919050565b614d5881614d23565b8114614d6357600080fd5b50565b600081359050614d7581614d4f565b92915050565b600060208284031215614d9157614d906130ff565b5b6000614d9f84828501614d66565b91505092915050565b600081600a0b9050919050565b614dbe81614da8565b8114614dc957600080fd5b50565b600081359050614ddb81614db5565b92915050565b600060208284031215614df757614df66130ff565b5b6000614e0584828501614dcc565b91505092915050565b600067ffffffffffffffff82169050919050565b614e2b81614e0e565b8114614e3657600080fd5b50565b600081359050614e4881614e22565b92915050565b600060208284031215614e6457614e636130ff565b5b6000614e7284828501614e39565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000614ea682614e7b565b9050919050565b614eb681614e9b565b8114614ec157600080fd5b50565b600081359050614ed381614ead565b92915050565b600060208284031215614eef57614eee6130ff565b5b6000614efd84828501614ec4565b91505092915050565b60007fffffffffffffffffffffffff000000000000000000000000000000000000000082169050919050565b614f3b81614f06565b8114614f4657600080fd5b50565b600081359050614f5881614f32565b92915050565b600060208284031215614f7457614f736130ff565b5b6000614f8284828501614f49565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000082169050919050565b614fc081614f8b565b8114614fcb57600080fd5b50565b600081359050614fdd81614fb7565b92915050565b600060208284031215614ff957614ff86130ff565b5b600061500784828501614fce565b91505092915050565b60008160070b9050919050565b61502681615010565b811461503157600080fd5b50565b6000813590506150438161501d565b92915050565b60006020828403121561505f5761505e6130ff565b5b600061506d84828501615034565b91505092915050565b600070ffffffffffffffffffffffffffffffffff82169050919050565b61509c81615076565b81146150a757600080fd5b50565b6000813590506150b981615093565b92915050565b6000602082840312156150d5576150d46130ff565b5b60006150e3848285016150aa565b91505092915050565b60007cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82169050919050565b61511e816150ec565b811461512957600080fd5b50565b60008135905061513b81615115565b92915050565b600060208284031215615157576151566130ff565b5b60006151658482850161512c565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000082169050919050565b6151a38161516e565b81146151ae57600080fd5b50565b6000813590506151c08161519a565b92915050565b6000602082840312156151dc576151db6130ff565b5b60006151ea848285016151b1565b91505092915050565b60007fffffffffff00000000000000000000000000000000000000000000000000000082169050919050565b615228816151f3565b811461523357600080fd5b50565b6000813590506152458161521f565b92915050565b600060208284031215615261576152606130ff565b5b600061526f84828501615236565b91505092915050565b600065ffffffffffff82169050919050565b61529381615278565b811461529e57600080fd5b50565b6000813590506152b08161528a565b92915050565b6000602082840312156152cc576152cb6130ff565b5b60006152da848285016152a1565b91505092915050565b60008160040b9050919050565b6152f9816152e3565b811461530457600080fd5b50565b600081359050615316816152f0565b92915050565b600060208284031215615332576153316130ff565b5b600061534084828501615307565b91505092915050565b60006bffffffffffffffffffffffff82169050919050565b61536a81615349565b811461537557600080fd5b50565b60008135905061538781615361565b92915050565b6000602082840312156153a3576153a26130ff565b5b60006153b184828501615378565b91505092915050565b6153c381614e7b565b81146153ce57600080fd5b50565b6000813590506153e0816153ba565b92915050565b6000602082840312156153fc576153fb6130ff565b5b600061540a848285016153d1565b91505092915050565b60008160000b9050919050565b61542981615413565b811461543457600080fd5b50565b60008135905061544681615420565b92915050565b600060208284031215615462576154616130ff565b5b600061547084828501615437565b91505092915050565b600068ffffffffffffffffff82169050919050565b61549781615479565b81146154a257600080fd5b50565b6000813590506154b48161548e565b92915050565b6000602082840312156154d0576154cf6130ff565b5b60006154de848285016154a5565b91505092915050565b600081601c0b9050919050565b6154fd816154e7565b811461550857600080fd5b50565b60008135905061551a816154f4565b92915050565b600060208284031215615536576155356130ff565b5b60006155448482850161550b565b91505092915050565b6000819050919050565b6155608161554d565b811461556b57600080fd5b50565b60008135905061557d81615557565b92915050565b600060208284031215615599576155986130ff565b5b60006155a78482850161556e565b91505092915050565b6000819050919050565b6155c3816155b0565b81146155ce57600080fd5b50565b6000813590506155e0816155ba565b92915050565b6000602082840312156155fc576155fb6130ff565b5b600061560a848285016155d1565b91505092915050565b60008160010b9050919050565b61562981615613565b811461563457600080fd5b50565b60008135905061564681615620565b92915050565b600060208284031215615662576156616130ff565b5b600061567084828501615637565b91505092915050565b60007fffffffffffffffffffffffffffffff000000000000000000000000000000000082169050919050565b6156ae81615679565b81146156b957600080fd5b50565b6000813590506156cb816156a5565b92915050565b6000602082840312156156e7576156e66130ff565b5b60006156f5848285016156bc565b91505092915050565b600066ffffffffffffff82169050919050565b61571a816156fe565b811461572557600080fd5b50565b60008135905061573781615711565b92915050565b600060208284031215615753576157526130ff565b5b600061576184828501615728565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffffff00000000000000000082169050919050565b61579f8161576a565b81146157aa57600080fd5b50565b6000813590506157bc81615796565b92915050565b6000602082840312156157d8576157d76130ff565b5b60006157e6848285016157ad565b91505092915050565b600081601d0b9050919050565b615805816157ef565b811461581057600080fd5b50565b600081359050615822816157fc565b92915050565b60006020828403121561583e5761583d6130ff565b5b600061584c84828501615813565b91505092915050565b60008160130b9050919050565b61586b81615855565b811461587657600080fd5b50565b60008135905061588881615862565b92915050565b6000602082840312156158a4576158a36130ff565b5b60006158b284828501615879565b91505092915050565b60006fffffffffffffffffffffffffffffffff82169050919050565b6158e0816158bb565b81146158eb57600080fd5b50565b6000813590506158fd816158d7565b92915050565b600060208284031215615919576159186130ff565b5b6000615927848285016158ee565b91505092915050565b60008160050b9050919050565b61594681615930565b811461595157600080fd5b50565b6000813590506159638161593d565b92915050565b60006020828403121561597f5761597e6130ff565b5b600061598d84828501615954565b91505092915050565b60008115159050919050565b6159ab81615996565b81146159b657600080fd5b50565b6000813590506159c8816159a2565b92915050565b6000602082840312156159e4576159e36130ff565b5b60006159f2848285016159b9565b91505092915050565b60007fffffffffffffffffffffffffff0000000000000000000000000000000000000082169050919050565b615a30816159fb565b8114615a3b57600080fd5b50565b600081359050615a4d81615a27565b92915050565b600060208284031215615a6957615a686130ff565b5b6000615a7784828501615a3e565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffffffffff0000000000000000000082169050919050565b615ab581615a80565b8114615ac057600080fd5b50565b600081359050615ad281615aac565b92915050565b600060208284031215615aee57615aed6130ff565b5b6000615afc84828501615ac3565b91505092915050565b6000819050919050565b615b1881615b05565b8114615b2357600080fd5b50565b600081359050615b3581615b0f565b92915050565b600060208284031215615b5157615b506130ff565b5b6000615b5f84828501615b26565b91505092915050565b60007fffffffffffffffffffffffffffffffffffffff0000000000000000000000000082169050919050565b615b9d81615b68565b8114615ba857600080fd5b50565b600081359050615bba81615b94565b92915050565b600060208284031215615bd657615bd56130ff565b5b6000615be484828501615bab565b91505092915050565b60008160120b9050919050565b615c0381615bed565b8114615c0e57600080fd5b50565b600081359050615c2081615bfa565b92915050565b600060208284031215615c3c57615c3b6130ff565b5b6000615c4a84828501615c11565b91505092915050565b600071ffffffffffffffffffffffffffffffffffff82169050919050565b615c7a81615c53565b8114615c8557600080fd5b50565b600081359050615c9781615c71565b92915050565b600060208284031215615cb357615cb26130ff565b5b6000615cc184828501615c88565b91505092915050565b60008083601f840112615ce057615cdf6147cb565b5b8235905067ffffffffffffffff811115615cfd57615cfc6147d0565b5b602083019150836001820283011115615d1957615d186147d5565b5b9250929050565b60008060208385031215615d3757615d366130ff565b5b600083013567ffffffffffffffff811115615d5557615d54613104565b5b615d6185828601615cca565b92509250509250929050565b60008160020b9050919050565b615d8381615d6d565b8114615d8e57600080fd5b50565b600081359050615da081615d7a565b92915050565b600060208284031215615dbc57615dbb6130ff565b5b6000615dca84828501615d91565b91505092915050565b60007fffffffffffffffffffffff00000000000000000000000000000000000000000082169050919050565b615e0881615dd3565b8114615e1357600080fd5b50565b600081359050615e2581615dff565b92915050565b600060208284031215615e4157615e406130ff565b5b6000615e4f84828501615e16565b91505092915050565b60008160060b9050919050565b615e6e81615e58565b8114615e7957600080fd5b50565b600081359050615e8b81615e65565b92915050565b600060208284031215615ea757615ea66130ff565b5b6000615eb584828501615e7c565b91505092915050565b60008160110b9050919050565b615ed481615ebe565b8114615edf57600080fd5b50565b600081359050615ef181615ecb565b92915050565b600060208284031215615f0d57615f0c6130ff565b5b6000615f1b84828501615ee2565b91505092915050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b615f5981615f24565b8114615f6457600080fd5b50565b600081359050615f7681615f50565b92915050565b600060208284031215615f9257615f916130ff565b5b6000615fa084828501615f67565b9150509291505056fea264697066735822122097a8f2b25091ab92b6de6f7631336f24a6840fdd621a15d411e16f7dd7290c9e64736f6c63430008110033";
    const contractPromise = deploy(abi, bytecode);

    for (const { types, valueFunc } of tests) {
        for (const type of types) {
            const value = valueFunc(type);

            it(`tests typed value: Typed.from(${ type })`, async function() {
                this.timeout(10000);
                const { contract } = await contractPromise;

                const v = Typed.from(type, value);
                const result = await contract.testTyped(v);
                assert.equal(result, type);
            });

            it(`tests typed value: Typed.${ type }()`, async function() {
                this.timeout(10000);
                const { contract } = await contractPromise;

                const v = (<any>Typed)[type](value);
                const result = await contract.testTyped(v);
                assert.equal(result, type);
            });
        }
    }

});



/*
describe("Test Contract Calls", function() {
    it("finds typed methods", async function() {
        const contract = new Contract("0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72", [
            "function foo(string s) view returns (uint)",
            "function foo(uint8) view returns (uint)",
            "function foo(uint u, bool b) view returns (uint)",
        ]);
        const value = Typed.string("42");
        await contract.foo.populateTransaction(value, Typed.overrides({ value: 100 }))
        contract["foo(string)"].fragment
    });
});

describe("Test Contract Interface", function() {
    it("builds contract interfaces", async function() {
        this.timeout(60000);

        interface Erc20Interface {
            // Constant Methods
            balanceOf: ConstantContractMethod<[ address: string | Addressable ], bigint>;
            decimals: ConstantContractMethod<[ ], bigint>;

            name: ConstantContractMethod<[ ], string>;
            symbol: ConstantContractMethod<[ ], string>;

            // Mutation Methods
            transferFrom: ContractMethod<[ address: string | Addressable,
              address: string | Addressable, amount: BigNumberish ], boolean>;

            // Events
            filters: {
                Transfer: ContractEvent<[ from: Addressable | string, to: BigNumberish ]>;
            }
        }

        const erc20Abi = [
            "function balanceOf(address owner) view returns (uint)",
            "function decimals() view returns (uint)",
            "function name() view returns (string)",
            "function symbol() view returns (string)",

            "function transferFrom(address from, address to, uint amount) returns (boolean)",

            "event Transfer(address indexed from, address indexed to, uint amount)"
        ];

        class Erc20Contract extends BaseContract.buildClass<Erc20Interface>(erc20Abi) { };

        const provider = new providers.InfuraProvider();
        // ENS
        //const addr = "0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72";
        // DAI
        const addr = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
        const contract = new Erc20Contract(addr, provider);
        console.log("SYMBOL", await contract.symbol());
        console.log("DECIMALS", await contract.decimals());
        console.log(await contract.balanceOf("0x5555763613a12D8F3e73be831DFf8598089d3dCa"));
        console.log(await contract.balanceOf("ricmoo.eth"));

        await contract.on(contract.filters.Transfer, (from, to, value, event) => {
            console.log("HELLO!", { from, to, value, event });
            event.removeListener();
        });
        const logs = await contract.queryFilter("Transfer", -10);
        console.log(logs, logs[0], logs[0].args.from);
    });
});
*/

type TestContractFallbackResult = {
    data: string;
} | {
    error: string;
};

type TestContractFallback = {
    name: string;
    address: string;
    abi: Array<string>;
    sendNone: TestContractFallbackResult;
    sendData: TestContractFallbackResult;
    sendValue: TestContractFallbackResult;
    sendDataAndValue: TestContractFallbackResult;
};

describe("Test Contract Fallback", function() {
    const tests: Array<TestContractFallback> = [
        {
            name: "none",
            address: "0x0ccdace3d8353fed9b87a2d63c40452923ccdae5",
            abi: [ ],
            sendNone: { error: "no fallback" },
            sendData: { error: "no fallback" },
            sendValue: { error: "no fallback" },
            sendDataAndValue: { error: "no fallback" },
        },
        {
            name: "non-payable fallback",
            address: "0x3f10193f79a639b11ec9d2ab42a25a4a905a8870",
            abi: [
                "fallback()"
            ],
            sendNone: { data: "0x" },
            sendData: { data: "0x1234" },
            sendValue: { error: "overrides.value" },
            sendDataAndValue: { error: "overrides.value" },
        },
        {
            name: "payable fallback",
            address: "0xe2de6b97c5eb9fee8a47ca6c0fa642331e0b6330",
            abi: [
                "fallback() payable"
            ],
            sendNone: { data: "0x" },
            sendData: { data: "0x1234" },
            sendValue: { data: "0x" },
            sendDataAndValue: { data: "0x1234" },
        },
        {
            name: "receive-only",
            address: "0xf8f2afbbe37f6a4520e4db7f99495655aa31229b",
            abi: [
                "receive()"
            ],
            sendNone: { data: "0x" },
            sendData: { error: "overrides.data" },
            sendValue: { data: "0x" },
            sendDataAndValue: { error: "overrides.data" },
        },
        {
            name: "receive and payable fallback",
            address: "0x7d97ca5d9dea1cd0364f1d493252006a3c4e18a0",
            abi: [
                "fallback() payable",
                "receive()"
            ],
            sendNone: { data: "0x" },
            sendData: { data: "0x1234" },
            sendValue: { data: "0x" },
            sendDataAndValue: { data: "0x1234" },
        },
        {
            name: "receive and non-payable fallback",
            address: "0x5b59d934f0d22b15e73b5d6b9ae83486b70df67e",
            abi: [
                "fallback()",
                "receive()"
            ],
            sendNone: { data: "0x" },
            sendData: { data: "0x" },
            sendValue: { data: "0x" },
            sendDataAndValue: { error: "overrides" },
        },
    ];

    const networkName = "sepolia";
    const provider = getProvider("InfuraProvider", networkName);

    const testGroups: Array<{ group: "sendNone" | "sendData" | "sendValue" | "sendDataAndValue", tx: any }> = [
        {
            group: "sendNone",
            tx: { }
        },
        {
            group: "sendData",
            tx: { data: "0x1234" }
        },
        {
            group: "sendValue",
            tx: { value: 123 }
        },
        {
            group: "sendDataAndValue",
            tx: { data: "0x1234", value: 123 }
        },
    ];

    for (const { group, tx } of testGroups) {
        for (const test of tests) {
            const { name, address, abi } = test;
            const send = test[group];

            const contract = new Contract(address, abi, provider);
            it(`test contract fallback checks: ${ group } - ${ name }`, async function() {
                const func = async function() {
                    if (abi.length === 0) {
                        throw new Error("no fallback");
                    }
                    assert.ok(contract.fallback);
                    return await contract.fallback.populateTransaction(tx)
                };

                if ("data" in send) {
                    await func();
                    //const result = await func();
                    //@TODO: Test for the correct populated tx
                    //console.log(result);
                    assert.ok(true);
                } else {
                    await assert.rejects(func, function(error: any) {
                        if (error.message === send.error) { return true; }
                        if (isError(error, "INVALID_ARGUMENT")) {
                            return error.argument === send.error;
                        }
                        console.log("EE", error);
                        return true;
                    });
                }
            });
        }
    }
});

